class Vue{
    constructor(options){
        this.$el = options.el;
        this.$data = options.data;
        if(this.$el){
            //数据劫持
            new Observer(this.$data);
            //模板编译
            new Compile(this.$el,this);
        }
    }
}


//数据劫持

class Observer{
    constructor(data){
        this.observer(data);
    }

    observer(data){
        if(data && typeof data === 'object'){
            for(let key in data){
                this.observerData(data,key,data[key]);
            }
        }
    }

    /**
     * {
            user:{
                username:''
            },
            msg:'hello'
        }
     * 
    */

    observerData(data,key,value){
        //user {username:'zs}  msg  hello
        this.observer(value);
        let dep = new Dep();
        //对象 属性 set get 
        Object.defineProperty(data,key,{
            set(newVal){
                console.log("设置值");
                if(newVal != value){
                    value = newVal;
                    dep.notify();
                }
            },
            get(){
                Dep.target && dep.add(Dep.target);
                console.log("获取数据");
                return value
            }
        })
    }
}

class Dep{
    constructor(){
        this.watchers = [];
    }

    add(watcher){
        this.watchers.push(watcher);
    }

    notify(){
        console.log(this.watchers.length)
        this.watchers.forEach((watcher) => {
            watcher.update()
        })
    }
}


class Watcher{
    constructor(vm,expr,cb){
        this.vm = vm;
        this.expr = expr;
        this.cb = cb;
        //旧值
        this.oldVal = this.getVal();
    }

    //获取值
    getVal(){
        Dep.target = this;
        let value = compileUtil.getVal(this.expr,this.vm);
        Dep.target = null;
        return value
    }

    //更新函数
    update(){
        let newValue = compileUtil.getVal(this.expr,this.vm);
        if(newValue != this.oldVal){
            this.cb(newValue)
        }
    }

}

//vm.$watch(vm,'user.username',(newValue) => {})


//模板编译

class Compile{
    constructor(el,vm){
        this.vm = vm;
        this.el = this.isElementNode(el) ? el : document.querySelector(el);
        //把#app节点下的所有子节点放到文档片段内
        let fragment = this.nodeToFragment(this.el);
        console.log(fragment);
        //把模板和数据进行编译，初始化视图
        this.compile(fragment);

        this.el.appendChild(fragment);

    }

    //判断当前的属性是否是指令
    isDirective(attr){
        //判断字符串是否以指定字符开头
        return attr.startsWith('v-')
    }

    //编译元素节点 --->  指令 v-model ----> 获取元素节点属性
    compileElement(node){
        let attrs = node.attributes;
        [...attrs].forEach(attr => {
            console.dir(attr);
            //name:属性 type v-model value:属性值
            let {name,value:expr} = attr;
            if(this.isDirective(name)){
                //属性为指令  v-model  v-html v-text
                let [,directive] = name.split('-'); //[v,model]
                //node:当前的节点  expr:表达式  this.vm:vue实例
                compileUtil[directive](node,expr,this.vm)
            }
        })
    }

    //编译文本节点
    compileText(node){
        //获取文本节点的内容
        let content = node.textContent;
        console.log("====",content);
        //区分是否有花括号
        if(/\{\{(.+?)\}\}/.test(content)){
            compileUtil.content(node,content,this.vm);
        }   
    }

    //编译的方法
    compile(node){
        let childnodes = node.childNodes;
        [...childnodes].forEach(child => {
            console.log(child);
            //1.区分是元素节点还是文本节点

            if(this.isElementNode(child)){
                //编译元素节点
                this.compileElement(child);
                this.compile(child);
            }else{
                //编译文本节点
                this.compileText(child);
            }
        })
    }

    nodeToFragment(node){
        //创建文档片段
        //文档片段再内存中，不在主dom树内，不会引起页面的回流，节省性能
        let fragment = document.createDocumentFragment();

        let firstChild;
        //把node的第一个子节点赋值给firstChild，并移除的node的第一个子节点
        while(firstChild = node.firstChild){
            fragment.appendChild(firstChild)
        }
        console.log("======")
        return fragment
    }

    //获取当前节点是否为元素节点
    isElementNode(node){
        //1 :元素节点  8 ：注释节点  2：属性节点  3：文本。。。
        return node.nodeType === 1
    }
}

compileUtil = {
    //查找对应表达式的值给节点赋值
    //node:input  expr:user.username  
    //vm.$data {
    //     user:{
    //         username:'zs'
    //     },
    //     msg:'hello'
    // }
    getVal(expr,vm){
        return expr.split('.').reduce((pre,cur) => {
            return pre[cur]
        },vm.$data)
    },

    setVal(vm,expr,value){
        expr.split('.').reduce((pre,cur,index,arr) => {
            if(index === arr.length-1){
                pre[cur] = value
            }
            return pre[cur]
        },vm.$data)
    },  

    model(node,expr,vm){
        //node.value = 
        let val = this.getVal(expr,vm);
        let fn = this.updater.compileModel;
        fn(node,val);
        new Watcher(vm,expr,(newValue) => {
            fn(node,newValue);
        })
        node.addEventListener('input',(e) => {
            this.setVal(vm,expr,e.target.value)
        })
    },
    html(){

    },
    text(){

    },
    //替换{{}}对应表达式的值
    content(node,content,vm){
        //获取文本内部的表达式
        let fn = this.updater.compileContent;
        let value = content.replace(/\{\{(.+?)\}\}/g,(...argv) => {
            console.log(argv[1])
            new Watcher(vm,argv[1],(newValue) => {
                fn(node,newValue);
            })
            return this.getVal(argv[1],vm)
        })

        console.log(value)
        fn(node,value);
        
    },
    updater:{
        compileModel(node,value){
            node.value = value;
        },
        compileContent(node,value){
            node.textContent = value;
        }
    }
}